#pragma once
#include <string>
#include <iostream>
#include <fstream>
#include <filesystem> //c++17, 需要高标准编译器和-std=c++17
#include <unistd.h>
#include <ctime>
#include <stdio.h>
#include <sstream> //stringstream
#include <memory>

#include "Strategy.hpp"


namespace wzx
{

    enum class log_level
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std::string log_level_to_string(log_level level)
    {
        switch(level)
        {
            case log_level::DEBUG:
                return "DEBUG";
            case log_level::INFO:
                return "INFO";
            case log_level::WARNING:
                return "WARNING";
            case log_level::ERROR:
                return "ERROR";
            case log_level::FATAL:
                return "FATAL";
            default:
                return "UNKNOWN";
        }
    }

    std::string get_current_time()
    {
        //time.h库提供的结构体
        // struct tm {
        //     int tm_sec;    /* Seconds (0-60) */
        //     int tm_min;    /* Minutes (0-59) */
        //     int tm_hour;   /* Hours (0-23) */
        //     int tm_mday;   /* Day of the month (1-31) */
        //     int tm_mon;    /* Month (0-11) */
        //     int tm_year;   /* Year - 1900 */
        //     int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
        //     int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
        //     int tm_isdst;  /* Daylight saving time */
        //    };

        time_t timestamp = time(nullptr);//获取时间戳
        struct tm curr;
        localtime_r(&timestamp, &curr);//_r版为可重入函数
        char timebuffer[128] = {'\0'};
        snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d", 
                                                                                 curr.tm_year + 1900,
                                                                                 curr.tm_mon, 
                                                                                 curr.tm_mday,
                                                                                 curr.tm_hour, 
                                                                                 curr.tm_min, 
                                                                                 curr.tm_sec
                );
        return timebuffer;
    }





    class logger
    {
    public:
        class log_message //生成一条日志信息
        {
            // [可读性很好的时间] [日志等级] [进程pid] [打印对应日志的文件名] [行号] - 消息内容，支持可变参数
            // [2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world
        public:
            log_message(log_level level, const std::string& filename, const int line, logger& rlg)
                :_time(get_current_time())
                ,_level(level)
                ,_pid(getpid())
                ,_filename(filename)
                ,_line(line)
                ,_logger(rlg)
            {
                //法1:
                //通过stringstream流 将各种类型转化成string
                //单行日志的"前半部分": 转化+存储
                // std::stringstream ss;
                // ss << "[" << _time << "] "
                // << "[" << log_level_to_string(_level) << "] "
                // << "[" << _pid << "] "
                // << "[" << _filename << "] "
                // << "[" << _line << "] "
                // << "- ";

                // ss >> _log; //等于_log = ss.str();

                //法2:
                //把"前半部分"插入流对象中
                _ss_log << "[" << _time << "] "
                << "[" << log_level_to_string(_level) << "] "
                << "[" << _pid << "] "
                << "[" << _filename << "] "
                << "[" << _line << "] "
                << "- ";
            }

            
            template<typename T>
            log_message& operator<<(T&& data)
            {
                //法1: 单行日志的"后半部分": 转化+存储
                // std::stringstream ss;
                // ss << data;//等于ss += (str)data;   //添加把各种类型的data转化成字符串
                // ss >> _log;//等于_log = ss.str();会覆盖 -- err
                // _log += ss.str();//添加 -- ok

                //法2:
                _ss_log << data;

                return *this;//支持连续流插入
            }

            ~log_message()
            {
                if(_logger._strategy) //unique_ptr重载的operator bool, 指向为空return false
                {
                    //法1:
                    // _logger._strategy->refresh(_log);
                    
                    //法2:
                    _logger._strategy->refresh(_ss_log.str());
                }
            }



        private:
            std::string _time;
            log_level _level;
            pid_t _pid;
            std::string _filename;
            int _line;

            //法1: std::string _log;//合并后的一条完整的日志
            std::stringstream _ss_log;//直接用一个string流(同时支持i和o)存储完整日志

            logger& _logger;//支持RAII风格
        };





        //默认显示器策略
        logger()
            //:_strategy(new console_log_strategy)
            :_strategy(std::make_unique<console_log_strategy>())
        {}

        void switch_console()
        {
            //使用 std::make_unique 比直接使用 new 表达式更为安全，因为它可以防止资源泄漏，并提供异常安全保证。
            //_strategy = new console_log_strategy;
            auto ptr = new console_log_strategy;
            //  0x11111111 .. 
            // std::unique_ptr<console_log_strategy>&& 
            _strategy = std::make_unique<console_log_strategy>();
        }

        void switch_file()
        {
            _strategy = std::make_unique<file_log_strategy>();
        }

        // 故意返回拷贝, 后续log_message()匿名临时对象完成一次或多次<<(<<返回的是&, 可重复流插入该匿名对象)后, 即运行至下一行后, 会自动析构
        log_message operator()(log_level level, const std::string& filename, const int line)
        {
            return log_message(level, filename, line, *this/*把lg传过去*/);
        }        

    private:
        std::unique_ptr<log_strategy> _strategy; //日志写入策略
    };



    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //全局对象
    wzx::logger lg;//用一个全局对象来控制每行日志(log_message)的刷新时机 -- RAII风格

    //宏定义简化调用, 且包装.hpp文件内实现的全局对象
    #define LOG(level) wzx::lg(level, __FILE__, __LINE__)
    #define LOG_SWITCH_CONSOLE wzx::lg.switch_console()
    #define LOG_SWITCH_FILE wzx::lg.switch_file()

}  